package com.mobile.web.config;

import com.mobile.util.Const;
import com.mobile.web.config.jwt.JwtFilter;
import com.mobile.web.config.jwt.JwtRealm;
import com.mobile.web.config.jwt.JwtSubjectFactory;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.shiro.cache.Cache;
import org.apache.shiro.cache.CacheManager;
import org.apache.shiro.cache.MemoryConstrainedCacheManager;
import org.apache.shiro.mgt.DefaultSessionStorageEvaluator;
import org.apache.shiro.mgt.DefaultSubjectDAO;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.mgt.SubjectFactory;
import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.mgt.DefaultWebSubjectFactory;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.config.MethodInvokingFactoryBean;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;

import javax.servlet.DispatcherType;
import javax.servlet.Filter;
import java.util.LinkedHashMap;
import java.util.Map;

@Configuration
public class ShiroConfig {
	private Logger logger = LogManager.getLogger(ShiroConfig.class);
	/**
	 *  创建ShiroFilterFactoryBean
	 * @author cheetah
	 * @date 2020/11/21
	 * @return: org.apache.shiro.spring.web.ShiroFilterFactoryBean
	 */
	@Bean
	public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager){
		ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
		// 必须设置 SecurityManager
		shiroFilterFactoryBean.setSecurityManager(securityManager);
		//设置shiro内置过滤器
		Map<String, Filter> filters = new LinkedHashMap<>();
		//添加自定义过滤器：只对需要登陆的接口进行过滤
		filters.put("authc", new JwtFilter());
		//添加自定义过滤器：对权限进行验证
//        filters.put("roles", new CustomRolesOrAuthorizationFilter());
		shiroFilterFactoryBean.setFilters(filters);

		// setLoginUrl 如果不设置值，默认会自动寻找Web工程根目录下的"/login.jsp"页面 或 "/login" 映射
		shiroFilterFactoryBean.setLoginUrl("/login/toLogin");
		// 设置无权限时跳转的 url;
		shiroFilterFactoryBean.setUnauthorizedUrl("/notAuth");

		// 设置拦截器
		Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();


		//游客，开发权限
		filterChainDefinitionMap.put("/guest/**", "anon");
		//开放登陆/注销接口
		filterChainDefinitionMap.put("/login/toLogin", "anon");
		filterChainDefinitionMap.put("/docs.html", "anon");
		filterChainDefinitionMap.put("/swagger-ui/**", "anon");
		filterChainDefinitionMap.put("/swagger-ui.html", "anon");
		filterChainDefinitionMap.put("/v3/api-docs/**", "anon");
		filterChainDefinitionMap.put( "/webjars/**" , "anon");

		// 静态资源直接访问
		filterChainDefinitionMap.put("/noper/**", "anon");
		filterChainDefinitionMap.put("/word/**", "anon");
		filterChainDefinitionMap.put("/ppt/**", "anon");
		filterChainDefinitionMap.put("/video/**", "anon");
		filterChainDefinitionMap.put("/image/**", "anon");

		//其余接口一律拦截
		//主要这行代码必须放在所有权限设置的最后，不然会导致所有 url 都被拦截
		filterChainDefinitionMap.put("/**", "authc");

		shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
		logger.info("-------Shiro拦截器工厂类注入成功-----------");
		return shiroFilterFactoryBean;
	}

	/**
	 *  注入安全管理器
	 * @author cheetah
	 * @date 2020/11/21
	 * @return: java.lang.SecurityManager
	 */
	@Bean
	public DefaultWebSecurityManager securityManager(JwtRealm jwtRealm, SubjectFactory subjectFactory,
													 SessionManager sessionManager,
													 CacheManager cacheManager) {
		DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
		securityManager.setRealm(jwtRealm);

		//关闭shiro自带的session
		DefaultSubjectDAO subjectDAO = new DefaultSubjectDAO();
		DefaultSessionStorageEvaluator defaultSessionStorageEvaluator = new DefaultSessionStorageEvaluator();
		defaultSessionStorageEvaluator.setSessionStorageEnabled(false);
		subjectDAO.setSessionStorageEvaluator(defaultSessionStorageEvaluator);
		securityManager.setSubjectDAO(subjectDAO);
		securityManager.setSubjectFactory(subjectFactory);
		securityManager.setSessionManager(sessionManager);
		securityManager.setCacheManager(cacheManager);
		return securityManager;
	}

	/**
	 *  jwt身份认证和权限校验
	 * @author wbr
	 * @date 2022/11/24
	 * @return: com.cheetah.shiroandjwt.jwt.JwtRealm
	 */
	@Bean
	public JwtRealm jwtRealm() {
		JwtRealm jwtRealm = new JwtRealm();
		jwtRealm.setAuthenticationCachingEnabled(false);
		jwtRealm.setAuthorizationCachingEnabled(true);
		jwtRealm.setAuthorizationCacheName(Const.USER_AUTHOR_CACHE);
		return jwtRealm;
	}

	/**
	 * Subject工厂
	 *
	 * @return
	 */
	@Bean
	public DefaultWebSubjectFactory subjectFactory() {
		return new JwtSubjectFactory();
	}


	/**
	 * 会话管理器
	 *
	 * @return
	 */
	@Bean
	public DefaultWebSessionManager sessionManager() {
		DefaultWebSessionManager defaultSessionManager = new DefaultWebSessionManager();
		defaultSessionManager.setSessionValidationSchedulerEnabled(false);
		return defaultSessionManager;
	}

	/**
	 * 在生产环境中使用的基于简单内存的CacheManager @link CacheManager}实现。它不会导致内存泄漏，因为它会产生{@link Cache Cache}s，
	 * 后者根据运行时环境的内存*限制和垃圾收集行为自动调整大小。
	 * 此处根据实际情况可以替换成ehcache、redis等实现
	 *
	 * @return
	 */
	@Bean
	public MemoryConstrainedCacheManager cacheManager() {
		return new MemoryConstrainedCacheManager();
	}


	/**
	 * Shiro生命周期处理器
	 *
	 * @return
	 */
	@Bean
	public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
		return new LifecycleBeanPostProcessor();
	}


	/**
	 * 开启Shiro的注解(如@RequiresRoles,@RequiresPermissions),需借助SpringAOP扫描使用Shiro注解的类,并在必要时进行安全逻辑验证
	 * 配置以下两个bean(DefaultAdvisorAutoProxyCreator(可选)和AuthorizationAttributeSourceAdvisor)即可实现此功能
	 *
	 * @return
	 */
	@Bean
	@DependsOn({"lifecycleBeanPostProcessor"})
	public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator() {
		DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
		// 强制使用cglib
		advisorAutoProxyCreator.setProxyTargetClass(true);
		return advisorAutoProxyCreator;
	}

	@Bean
	public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
		AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
		authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
		return authorizationAttributeSourceAdvisor;
	}
	@Bean
	@SuppressWarnings({ "rawtypes", "unchecked" })
	public FilterRegistrationBean replaceTokenFilter(){
		FilterRegistrationBean registration = new FilterRegistrationBean();
		registration.setDispatcherTypes(DispatcherType.REQUEST);
		registration.setFilter(new crosFilter());
		registration.addUrlPatterns("/*");
		registration.setName("crosFilter ");
		registration.setOrder(1);
		return registration;
	}
	/**
	 * 相当于调用SecurityUtils.setSecurityManager(securityManager)
	 *
	 * @return
	 */
	@Bean
	public MethodInvokingFactoryBean methodInvokingFactoryBean(DefaultWebSecurityManager securityManager) {
		MethodInvokingFactoryBean methodInvokingFactoryBean = new MethodInvokingFactoryBean();
		methodInvokingFactoryBean.setStaticMethod("org.apache.shiro.SecurityUtils.setSecurityManager");
		methodInvokingFactoryBean.setArguments(securityManager);
		return methodInvokingFactoryBean;
	}
}
